/*
 * Copyright (c) 2011, grossmann
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of the jo-widgets.org nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

package joodin.impl.widgets.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import joodin.impl.widgets.VaadinControl;

import org.jowidgets.common.image.IImageConstant;
import org.jowidgets.common.types.Markup;
import org.jowidgets.common.types.SelectionPolicy;
import org.jowidgets.common.widgets.controler.ITreeNodeListener;
import org.jowidgets.spi.impl.controler.TreeSelectionObservableSpi;
import org.jowidgets.spi.widgets.ITreeNodeSpi;
import org.jowidgets.spi.widgets.ITreeSpi;
import org.jowidgets.spi.widgets.controler.ITreeSelectionListenerSpi;
import org.jowidgets.spi.widgets.setup.ITreeSetupSpi;

import com.google.gwt.user.client.ui.TreeItem;
import com.vaadin.data.Item;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.ui.Component.Event;
import com.vaadin.ui.Tree;
import com.vaadin.ui.Tree.CollapseEvent;
import com.vaadin.ui.Tree.CollapseListener;
import com.vaadin.ui.Tree.ExpandEvent;
import com.vaadin.ui.Tree.ExpandListener;

public class TreeImpl extends VaadinControl implements ITreeSpi, ITreeNodeSpi {

	private final boolean multiSelection;
	private final Map<Item, TreeNodeImpl> items;
	private final TreeSelectionObservableSpi treeObservable;

	private List<Item> lastSelection;

	@SuppressWarnings("serial")
	public TreeImpl(final Object parentUiReference, final ITreeSetupSpi setup) {
		super(new Tree());
		this.lastSelection = new LinkedList<Item>();
		this.treeObservable = new TreeSelectionObservableSpi();
		this.items = new HashMap<Item, TreeNodeImpl>();
		
		this.multiSelection = setup.getSelectionPolicy() == SelectionPolicy.MULTI_SELECTION;
		getUiReference().setSizeFull();
			
		getUiReference().addListener(new ExpandListener(){
			@Override
			public void nodeExpand(ExpandEvent event) {
				fireExpandedChanged(event, true);	
			}
		});
		
		getUiReference().addListener(new CollapseListener() {
			@Override
			public void nodeCollapse(CollapseEvent event) {
				fireExpandedChanged(event, false);
			}
		});
			
		getUiReference().addListener(new ItemClickListener() {
			
			@Override
			public void itemClick(ItemClickEvent event) {
				List<Item> items = new ArrayList<Item>();
				Item item = event.getItem();
				items.add(item);
				fireSelectionChange(items);
			}
		});
		
	}
	
	private void fireExpandedChanged(final Event event, final boolean expanded) {
		final TreeNodeImpl itemImpl = items.get(event.getComponent());
		if (itemImpl != null) {
			itemImpl.fireExpandedChanged(expanded);
		}
		else {
//			throw new IllegalStateException("No item impl registered for item '"
//				+ event.getComponent()
//				+ "'. This seems to be a bug");
		}
	}

	@Override
	public Tree getUiReference() {
		return (Tree) super.getUiReference();
	}

	@Override
	public ITreeNodeSpi getRootNode() {
		return this;
	}

	@Override
	public List<ITreeNodeSpi> getSelectedNodes() {
		final List<ITreeNodeSpi> result = new LinkedList<ITreeNodeSpi>();
		for (final Item item : lastSelection) {
			result.add(items.get(item));
		}
		return result;
	}

	@Override
	public ITreeNodeSpi addNode(final Integer index) {
		final TreeNodeImpl result = new TreeNodeImpl(this, null, index);
		registerItem(result.getUiReference(), result);
		return result;
	}

	@Override
	public void removeNode(final int index) {
		
	}


	@Override
	public void setMarkup(final Markup markup) {
		throw new UnsupportedOperationException("setMarkup is not possible on the root node");
	}

	@Override
	public void setExpanded(final boolean expanded) {
		throw new UnsupportedOperationException("setExpanded is not possible on the root node");
	}

	@Override
	public boolean isExpanded() {
		throw new UnsupportedOperationException("isExpanded is not possible on the root node");
	}

	@Override
	public void setSelected(final boolean selected) {
		throw new UnsupportedOperationException("setSelected is not possible on the root node");
	}

	@Override
	public boolean isSelected() {
		throw new UnsupportedOperationException("isSelected is not possible on the root node");
	}

	@Override
	public void setText(final String text) {
		throw new UnsupportedOperationException("setText is not possible on the root node");
	}

	@Override
	public void setToolTipText(final String text) {
		throw new UnsupportedOperationException("setToolTipText is not possible on the root node");
	}

	@Override
	public void setIcon(final IImageConstant icon) {
		throw new UnsupportedOperationException("setIcon is not possible on the root node");
	}

	@Override
	public void addTreeNodeListener(final ITreeNodeListener listener) {
		throw new UnsupportedOperationException("addTreeNodeListener is not possible on the root node");
	}

	@Override
	public void removeTreeNodeListener(final ITreeNodeListener listener) {
		throw new UnsupportedOperationException("removeTreeNodeListener is not possible on the root node");
	}
	
	@Override
	public void addTreeSelectionListener(ITreeSelectionListenerSpi listener) {
		treeObservable.addTreeSelectionListener(listener);
	}

	@Override
	public void removeTreeSelectionListener(ITreeSelectionListenerSpi listener) {
		treeObservable.removeTreeSelectionListener(listener);
	}

	protected void registerItem(final Item item, final TreeNodeImpl treeNodeImpl) {
		items.put(item, treeNodeImpl);
	}

	protected void unRegisterItem(final Item item) {
		items.remove(item);
	}

	private void fireSelectionChange(final List<Item> newSelection) {
		final List<Item> newSelectionList = newSelection;

		for (final Item wasSelected : lastSelection) {
			if (!newSelectionList.contains(wasSelected)) {
				items.get(wasSelected).fireSelectionChanged(false);
			}
		}

		for (final Item isSelected : newSelectionList) {
			if (!lastSelection.contains(isSelected)) {
				items.get(isSelected).fireSelectionChanged(true);
			}
		}

		lastSelection = newSelectionList;
		treeObservable.fireSelectionChanged();
	}
}
